package com.example.service;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolver;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class NettyServiceImpl extends ChannelInitializer implements NettyService, InitializingBean, CommandLineRunner, DisposableBean {

    Logger logger = LoggerFactory.getLogger(getClass());

    private NioEventLoopGroup group = new NioEventLoopGroup();

    private Bootstrap bootstrap;

    @Value("${netty.host}")
    private String host;

    @Value("${netty.port}")
    private int port;

    private int count = 0;

    @Override
    public void destroy() throws Exception {
        this.disConnect();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        this.bootstrap = new Bootstrap().group(this.group).option(ChannelOption.TCP_NODELAY, true).channel(NioSocketChannel.class).handler(this);
    }

    @Override
    public void run(String... args) throws Exception {
        this.connect();
    }

    @Override
    protected void initChannel(Channel ch) throws Exception {
        ClassResolver classResolver = ClassResolvers.cacheDisabled(getClass().getClassLoader());
        ch.pipeline().addLast("decoder", new ObjectDecoder(classResolver));
        ch.pipeline().addLast("encoder", new ObjectEncoder());
        ch.pipeline().addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
            @Override
            public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
                super.channelRegistered(ctx);
            }

            @Override
            public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
                super.channelUnregistered(ctx);
            }

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                super.channelActive(ctx);
            }

            @Override
            public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                super.channelInactive(ctx);
                logger.warn("NettyServiceImpl channelInactive ...");
            }

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                super.channelRead(ctx, msg);
            }

            @Override
            public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
                super.channelReadComplete(ctx);
            }

            @Override
            public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                logger.warn("NettyServiceImpl userEventTriggered ...");
            }

            @Override
            public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
                super.channelWritabilityChanged(ctx);
            }

            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                super.exceptionCaught(ctx, cause);
            }
        });
    }

    @Override
    public void connect() {
        ChannelFuture channelFuture = null;
        try {
            channelFuture = this.bootstrap.connect(this.host, this.port).sync();
            if (channelFuture.isSuccess()) {
                this.count = 0;
                logger.warn("netty client connect success ....");
            } else {
                count++;
                Thread.sleep(count * 5000);
                connect();
            }
        } catch (Exception e) {
            logger.warn("netty client connect error ....");
            count++;
            try {
                Thread.sleep(count * 5000);
            } catch (InterruptedException e1) {
                e1.printStackTrace();
            }
            connect();
        }
    }

    @Override
    public void disConnect() {
        this.bootstrap.clone();
    }
}
